package fm

import (
	"bytes"
	"fmt"
	"gitee.com/xluomo945/fm/helper"
	"github.com/gin-gonic/gin"
	"gopkg.in/yaml.v3"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"
)

type RegisterConfigHandle func(BindConfig) error
type BindConfig func(name string, vars interface{}) error

type config struct {
	filePath  string
	allowType []string
	data      map[interface{}]interface{}
	ext       string
	filename  string
	dir       string
}

var (
	// 支持的配置文件类型
	fileConfigExt = []string{".yaml"}
	// 默认运行端口
	runPort = "54321"
)

const (
	appMode = "FM_SERVER_RUN_MODE"
	appPort = "FM_SERVER_RUN_PORT"
)

func (app *App) loadConfig() error {
	// 设置基础默认配置
	app.defaultConfig()

	// 没有传递配置文件地址, 按无配置处理, 不报错终止
	if helper.StringEmpty(app.ConfigurationConfig.Path) {
		log.Println("[Warn] config path is empty")
		return nil
	}

	app.config = &config{
		filePath:  app.ConfigurationConfig.Path,
		allowType: fileConfigExt,
	}
	app.config.buildConfigPathInfo()

	// 检查配置是否存在
	if !helper.PathIsExists(app.config.filePath) {
		// 尝试按环境绑定配置
		envConfigPath := app.config.buildEnvConfigPath(app.mode)
		if !helper.PathIsExists(envConfigPath) {
			return fmt.Errorf("config not exists")
		}
		app.config.filePath = envConfigPath
		app.config.buildConfigPathInfo()
	}

	// 检查是否是受支持的配置文件类型
	if !app.config.checkConfigExt() {
		return fmt.Errorf("config ext is invalid")
	}

	// 读取配置文件
	if err := app.config.loadFileConfigData(); err != nil {
		return err
	}

	// 调用绑定函数
	if app.RegisterConfig != nil {
		if err := app.RegisterConfig(app.config.bindConfig); err != nil {
			return err
		}
	}

	return app.config.clear()
}

func (app *App) defaultConfig() {
	// 设置运行模式
	app.mode = os.Getenv(appMode)
	gin.SetMode(gin.ReleaseMode)

	// 创建GIN
	app.server = gin.New()

	// 设置运行端口
	app.runServerPort = fmt.Sprintf(":%s", helper.StringDefault(os.Getenv(appPort), runPort))
}

func (c *config) clear() error {
	c.data = nil
	// ......
	return nil
}

func (c *config) replaceEnvConfig() ([]byte, error) {
	buf, err := os.ReadFile(c.filePath)
	if err != nil {
		return nil, err
	}
	pattern, err := regexp.Compile(`\$\{\s*\w+\s*}`)
	if err != nil {
		return nil, err
	}
	result := pattern.FindAll(buf, -1)
	for k := range result {
		envKey := strings.TrimSpace(strings.TrimLeft(strings.TrimRight(string(result[k]), "}"), "${"))
		v := os.Getenv(envKey)
		buf = bytes.Replace(buf, result[k], []byte(v), -1)
	}
	return buf, nil
}

func (c *config) buildConfigPathInfo() {
	c.ext = filepath.Ext(c.filePath)
	c.filename = strings.Trim(filepath.Base(c.filePath), c.ext)
	c.dir = filepath.Dir(c.filePath)
}

func (c *config) buildEnvConfigPath(mode string) string {
	return fmt.Sprintf("%s%s%s-%s%s", c.dir, helper.DS, c.filename, mode, c.ext)
}

func (c *config) checkConfigExt() bool {
	for k := range fileConfigExt {
		if fileConfigExt[k] == c.ext {
			return true
		}
	}
	return false
}

func (c *config) loadFileConfigData() error {
	// 读取配置
	data, err := c.replaceEnvConfig()
	if err != nil {
		return fmt.Errorf("load config: %s", err)
	}
	return yaml.Unmarshal(data, &c.data)
}

func (c *config) bindConfig(name string, vars interface{}) error {
	data, ok := c.data[name]
	if !ok {
		return fmt.Errorf("config name [%s] not exists", name)
	}

	tmp, err := yaml.Marshal(data)
	if err != nil {
		return err
	}

	return yaml.Unmarshal(tmp, vars)
}
